1– Download the data
The GEO web app was chosen in lieu of R packages to look for an appropriate dataset.
As recorded in my journal:
“I selected Homo sapiens as organism, put publication date from 2017 January through 2020 December, specified sample counts from 6 to 12, and the presence of a supplementary file in TXT format. In the actual search query, I used ‘(gse[filter]) neural’.”
From the results, on page 2 as of 2020-02-02 I selected GSE127268, which was a heterozygous dataset, then thought better and selected GSE120200, a sister homozygous dataset because homozygous data should result in more pronounced differential expression.
Some basic info about the dataset from GEO, using code adapted from Slide 17 in Lecture 4:
gse <- getGEO("GSE120200",GSEMatrix=FALSE)
current_gpl <- names(GPLList(gse))[1]
current_gpl_info <- Meta(getGEO(current_gpl))
Platform title: Illumina NextSeq 500 (Homo sapiens)
Submission date Apr 15 2014
Last update date: Mar 26 2019
Organisms: Homo sapiens
To summarize the paper, they collected wild type cultured human neurons and edited cell lines that knocked out the ASXL1 gene. The knockout was shown to interfere with neural crest formation. Part of the experiment was global RNA sequencing to find what changes in expression occur after ASXL1 is knocked out.
Note that inside the paper, chicken embryos were used at various stages of experimentation to demonstrate migration. This dataset, however, only contains raw counts from high-thoroughput RNA sequencing of cultured human neurons.
Using the code from slide 56 of Lecture 3:
sfiles <- getGEOSuppFiles('GSE120200')
trying URL 'https://ftp.ncbi.nlm.nih.gov/geo/series/GSE120nnn/GSE120200/suppl//GSE120200_ExpressionMatrix.txt.gz?tool=geoquery'
Content type 'application/x-gzip' length 639872 bytes (624 KB)
==================================================
downloaded 624 KB
counts <- read.delim(rownames(sfiles)[1],header=TRUE, check.names = FALSE)
counts_matrix <- counts[,-1]
rownames(counts_matrix) <- counts[,1]
head(counts)
head(counts_matrix)
2- overview statistics
#are there any duplicates? Code adapted from Lecture 4 slide 23
summarized_gene_counts <- sort(table(counts$Geneid),decreasing = TRUE)
length(summarized_gene_counts[which(summarized_gene_counts>1)])
[1] 0
This means all the ensembl IDs are identical.
How many genes are there?
dim(counts)
[1] 47817 11
Are they all genes?
tail(counts)
NA
No, ERCC isn’t an Ensembl gene ID.
So what is ERCC? It seems like a quality control.
#Did we get any reads in the controls?
sum(rowSums(counts[grep("ERCC", counts$Geneid),-1]))
[1] 0
It looks like all of them will be filtered out for having a low count of 0, so they can be ignored safely. This also shows that there wasn’t any external contamination found using the ERCC kit.
Filtering out low counts using code adapted from Lecture 4 Slide 25:
cpms <- cpm(counts[,-1])
rownames(cpms) <- counts[,1]
#We have 3 'normal' samples and 7 'diseased' samples so let's use 3, since that is the smallest group of replicates.
keep <- rowSums(cpms > 1) >= 3
counts_filtered <- counts[keep,]
#delete autogenerated rownames left over from counts
rownames(counts_filtered) = c()
dim(counts_filtered)
[1] 15845 11
~Two thirds of the gene ids were removed with this filtering step.
Now to make graphs. Adapting the code from Slide 36 in Lecture 4:
#Boxplot:
boxplot_data <- log2(cpm(counts_filtered[,-1]))
rownames(boxplot_data) = counts_filtered[,1]
boxplot(boxplot_data, xlab = "Samples", ylab = "log2 CPM",
las = 2, cex = 0.5, cex.lab = 0.5,
cex.axis = 0.5, main = "ASXL1 RNASeq Samples")
abline(h = median(apply(boxplot_data, 2, median)), col = "orangered", lwd = 0.6, lty = "dashed")

Strange that there are outliers of -inf. This means that there has to be 0 values in the dataset after filtering step.
min(boxplot_data)
[1] -Inf
min(counts_filtered[,-1])
[1] 0
IT turns out that we didn’t filter out all low expressions. There are 0s in some but not all of the experiments for a particular gene. It is probably inconsequential.
Density plot: (Using code adapted from Lecture 4 slide 38)
counts_density <- apply(log2(cpm(counts_filtered[,-1])), 2, density)
xlim <- 0; ylim <- 0
for (i in 1:length(counts_density)) {
xlim <- range(c(xlim, counts_density[[i]]$x));
ylim <- range(c(ylim, counts_density[[i]]$y))
}
cols <- rainbow(length(counts_density))
ltys <- rep(1, length(counts_density))
plot(counts_density[[1]], xlim=xlim, ylim=ylim, type="n",
ylab="Smoothing density of log2-CPM", main="", cex.lab = 0.85)
#plot each line
for (i in 1:length(counts_density)) lines(counts_density[[i]], col=cols[i], lty=ltys[i])
#create legend
legend("topright", colnames(boxplot_data),
col=cols, lty=ltys, cex=0.75,
border ="red", text.col = "green4",
merge = TRUE, bg = "gray80")

The different samples look pretty close to each other, with the exception of WT2, but even that is still a reasonable deviation in shape.
3- convert to HUGO Gene Nomenclature Committee symbols
conversion_stash <- "data_id_conversion.rds"
if(file.exists(conversion_stash)){
data_id_conversion <- readRDS(conversion_stash)
} else {
ensembl <- useMart("ensembl")
data_id_conversion <- getBM(attributes = c("ensembl_gene_id","hgnc_symbol"),
filters = c("ensembl_gene_id"),
values = counts$Geneid, #use IDs from before the filtering stage
mart = ensembl)
saveRDS(data_id_conversion, conversion_stash)
}
head(data_id_conversion)
If the ensembl servers are nonoperational I have included a file called “data_id_conversion_backup.rds” which can be renamed to “data_id_conversion.rds” to be able to proceed.
#Duplicate hgnc symbols?
summarized_hgnc_counts <- sort(table(data_id_conversion$hgnc_symbol),decreasing = TRUE)
summarized_hgnc_counts[which(summarized_hgnc_counts>1)[1:10]]
BTNL2 DDX39B DHX16 FLOT1 GNL1 HLA-A HLA-DMA HLA-DMB HLA-DPA1
10897 8 8 8 8 8 8 8 8 8
dim(summarized_hgnc_counts[which(summarized_hgnc_counts>1)])
[1] 326
#Duplicate ensemble ids?
summarized_ensemble_counts <- sort(table(data_id_conversion$ensembl_gene_id),decreasing = TRUE)
summarized_ensemble_counts[which(summarized_ensemble_counts>1)[1:2]]
ENSG00000187510 ENSG00000230417
2 2
dim(summarized_ensemble_counts[which(summarized_ensemble_counts>1)])
[1] 2
There are 326 duplicated hgnc symbols when mapped from ensembl gene ids.
According to the Ensembl website as well as the HGNC website, at least for BTNL2, there were multiple splices for it that corresponded to all the different Ensembl gene ids.
In such a case, according to MacDonald (2016) on Bioconductor Forums if the transcript lengths were significantly different, summing up all the reads wouldn’t necessarily be the correct decision. There is a good chance, however, that the genes in question do not necessarily have reads, and in the step where we remove all the entries that have low counts they might not exist anymore. Note that we made the gene id mapping using the ids before the filtering step.
Essentially like in slide 24 of Lecture 4, the problem is ignored in the hopes that it doesn’t actually have to be dealt with.
There are also 2 duplicated ensembl ids in the mapping.
Since this isn’t a high number, we can look at them manually and see what happened:
dups <- which(summarized_ensemble_counts>1)
data_id_conversion[names(dups)[1] == data_id_conversion$ensembl_gene_id | names(dups)[2] == data_id_conversion$ensembl_gene_id,]
Googling C12orf74, it seems to be an older symbol. I presume the later entry for both of these duplicated IDs is the newer symbol.
4 - Normalization
Apply TMM by using edgeR. TMM was picked arbitrarily because it was what was used in lecture, it is specialized for RNA-seq, and in addition, it is what I usually hear about in discussions of normalization.
counts_filtered_matrix <- as.matrix(counts_filtered[,-1])
rownames(counts_filtered_matrix) <- counts_filtered$Geneid
samples <- c(rep("WT", 3), rep ("HOM", 7))
d = DGEList(counts=counts_filtered_matrix, group=samples)
d = calcNormFactors(d)
normalized_filtered_counts <- cpm(d)
normalized_counts_density <- apply(log2(normalized_filtered_counts), 2, density)
xlim <- 0; ylim <- 0
for (i in 1:length(normalized_counts_density)) {
xlim <- range(c(xlim, normalized_counts_density[[i]]$x));
ylim <- range(c(ylim, normalized_counts_density[[i]]$y))
}
cols <- rainbow(length(normalized_counts_density))
ltys <- rep(1, length(normalized_counts_density))
par(mfrow=c(2,2))
plot(normalized_counts_density[[1]], xlim=xlim, ylim=ylim, type="n",
ylab="Smoothing density of log2-CPM", main="Normalized", cex.lab = 0.85)
#plot each line
for (i in 1:length(normalized_counts_density)) lines(normalized_counts_density[[i]], col=cols[i], lty=ltys[i])
#create legend
legend("topright", colnames(boxplot_data),
col=cols, lty=ltys, cex=0.75,
border ="red", text.col = "green4",
merge = TRUE, bg = "gray80")
#compare to old plot
plot(counts_density[[1]], xlim=xlim, ylim=ylim, type="n",
ylab="Smoothing density of log2-CPM", main="Not Normalized", cex.lab = 0.85)
#plot each line
for (i in 1:length(counts_density)) lines(counts_density[[i]], col=cols[i], lty=ltys[i])
#create legend
legend("topright", colnames(boxplot_data),
col=cols, lty=ltys, cex=0.75,
border ="red", text.col = "green4",
merge = TRUE, bg = "gray80")
#boxplots
#Boxplot:
boxplot_data_normalized <- log2(normalized_filtered_counts)
boxplot(boxplot_data_normalized, xlab = "Samples", ylab = "log2 CPM",
las = 2, cex = 0.5, cex.lab = 0.5,
cex.axis = 0.5, main = "Normalized")
abline(h = median(apply(boxplot_data_normalized, 2, median)), col = "orangered", lwd = 0.6, lty = "dashed")
#old boxplot
boxplot(boxplot_data, xlab = "Samples", ylab = "log2 CPM",
las = 2, cex = 0.5, cex.lab = 0.5,
cex.axis = 0.5, main = "Not Normalized")
abline(h = median(apply(boxplot_data, 2, median)), col = "orangered", lwd = 0.6, lty = "dashed")

Looking at these plots, there isn’t much, if any difference. Nothing striking like what was presented in lecture.
plotMDS(d, labels=rownames(samples),
col = c("darkgreen","blue")[factor(samples)])
model_design <- model.matrix(~samples)
d <- estimateDisp(d, model_design)
par(mfrow=c(1,2))

plotBCV(d,col.tagwise = "black",col.common = "red")
plotMeanVar(d, show.raw.vars = TRUE,
show.tagwise.vars=TRUE, NBline=TRUE,
show.ave.raw.vars = TRUE,show.binned.common.disp.vars = TRUE)

The MDS plot looks good, WT and HOM are well separated into different quadrants. The BCV plot and mean-variance plot look similar to the ones in the slides, so they are probably ok as well.
5 - Apply identifier mapping
normalized_filtered_counts_annot <- merge(data_id_conversion, normalized_filtered_counts, by.x=1, by.y=0, all.y=TRUE)
#final_data_frame <- as.data.frame(normalized_filtered_counts)
ensembl_id_missing_gene <- normalized_filtered_counts_annot$ensembl_gene_id[
which(is.na(normalized_filtered_counts_annot$hgnc_symbol) | normalized_filtered_counts_annot$hgnc_symbol == '')]
#How many ensembl ids don't have symbols?
length(ensembl_id_missing_gene)
[1] 1190
hgnc_table <- normalized_filtered_counts_annot[which(!(normalized_filtered_counts_annot$ensembl_gene_id %in% ensembl_id_missing_gene)),]
#any NAs left?
which(is.na(hgnc_table$hgnc_symbol))
integer(0)
#any duplicates in ensembl gene ids?
duplicated(hgnc_table$ensemble_gene_id)
logical(0)
#any duplicates in hgnc ids?
length(which(duplicated(hgnc_table$hgnc_symbol)))
[1] 0
#if there were duplicates, this is how I would've summed them up, against the advice of MacDonald:
#unique_hgnc <- aggregate(hgnc_table[,3:12], list(hgnc_table[,2]), FUN="sum")
The idea to use the aggregate function came from “Jimbou” (2015) answering a question on Biostars.
There are 0 duplicates of HGNC symbols that we need to resolve. Lucky us!
Now that all our worries have been miraculously solved by the filtering step except for 7.5102556% of the dataset being removed due to lack of HGNC symbols, we can create the final data frame.
final_a1_data = as.data.frame(hgnc_table[3:12])
rownames(final_a1_data) <- hgnc_table[,2]
sample(final_a1_data, 10)
Final questions
I already answered some of these questions above, but I will formalize them here anyways.
What are the control and test conditions of the dataset?
Control condition: wild type ASXL1 gene in human induced pluripotent stem cell differentiated neural crest progenitors. Test condition: CRISPR/Cas9 edited ASXL1 gene (knockout) in human embryonic stem cell differentiated neural crest progenitors.
Why is the dataset of interest to you?
The dataset is of interest because the mechanism of neural cell migration is interesting to me. Understanding this mechanism has implications for brain development and therefore the basis of human intelligence.
Were there expression values that were not unique for specific genes? How did you handle these?
After the filtering step, there were no expression values that were not unique. I would’ve summed them without adjustments, against the recommendations of people on bioinformatics forums, assuming they were alternative splices of transcripts of similar length.
Were there expression values that could not be mapped to current HUGO symbols?
There were many expression values that could not be mapped to current HUGO symbols. Most of them appear to be in non-coding regions so I am not worried.
How many outliers were removed?
I didn’t remove any outliers of extremely high expression, but almost two-thirds of the dataset was removed because of low expression.
How did you handle replicates?
Since the experiment was relatively simple, I simply made WT and HOM groups for control and experimental samples when analyzing using edgeR.
What is the final coverage of your dataset?
Out of 47725 genes we started with, we ended up with 14655 genes. That is 30.7071765% coverage.
LS0tCnRpdGxlOiBBc3NpZ25tZW50IDEgTm90ZWJvb2sKYXV0aG9yOiBZaSBGZWkgSHVhbmcKZGF0ZTogMjAyMC0wMi0wNApvdXRwdXQ6IGh0bWxfbm90ZWJvb2sKLS0tCiMjIDAtIExvYWQgbGlicmFyaWVzCmBgYHtSLCBlcnJvcj1GQUxTRSwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KbGlicmFyeSgiR0VPcXVlcnkiKQpsaWJyYXJ5KCJiaW9tYVJ0IikKbGlicmFyeSgiZWRnZVIiKQpgYGAKCgojIyAx4oCTIERvd25sb2FkIHRoZSBkYXRhCgpUaGUgR0VPIHdlYiBhcHAgd2FzIGNob3NlbiBpbiBsaWV1IG9mIFIgcGFja2FnZXMgdG8gbG9vayBmb3IgYW4gYXBwcm9wcmlhdGUgZGF0YXNldC4KCkFzIHJlY29yZGVkIGluIG15IGpvdXJuYWw6CgoiSSBzZWxlY3RlZCBIb21vIHNhcGllbnMgYXMgb3JnYW5pc20sIHB1dCBwdWJsaWNhdGlvbiBkYXRlIGZyb20gMjAxNyBKYW51YXJ5IHRocm91Z2ggMjAyMCBEZWNlbWJlciwgc3BlY2lmaWVkIHNhbXBsZSBjb3VudHMgZnJvbSA2IHRvIDEyLCBhbmQgdGhlIHByZXNlbmNlIG9mIGEgc3VwcGxlbWVudGFyeSBmaWxlIGluIFRYVCBmb3JtYXQuCkluIHRoZSBhY3R1YWwgc2VhcmNoIHF1ZXJ5LCBJIHVzZWQgJyhnc2VbZmlsdGVyXSkgbmV1cmFsJy4iCgpGcm9tIHRoZSByZXN1bHRzLCBvbiBwYWdlIDIgYXMgb2YgMjAyMC0wMi0wMiBJIHNlbGVjdGVkIEdTRTEyNzI2OCwgd2hpY2ggd2FzIGEgaGV0ZXJvenlnb3VzIGRhdGFzZXQsIHRoZW4gdGhvdWdodCBiZXR0ZXIgYW5kIHNlbGVjdGVkIEdTRTEyMDIwMCwgYSBzaXN0ZXIgaG9tb3p5Z291cyBkYXRhc2V0IGJlY2F1c2UgaG9tb3p5Z291cyBkYXRhIHNob3VsZCByZXN1bHQgaW4gbW9yZSBwcm9ub3VuY2VkIGRpZmZlcmVudGlhbCBleHByZXNzaW9uLgoKU29tZSBiYXNpYyBpbmZvIGFib3V0IHRoZSBkYXRhc2V0IGZyb20gR0VPLCB1c2luZyBjb2RlIGFkYXB0ZWQgZnJvbSBTbGlkZSAxNyBpbiBMZWN0dXJlIDQ6CmBgYHtSLCBtZXNzYWdlPUZBTFNFfQpnc2UgPC0gZ2V0R0VPKCJHU0UxMjAyMDAiLEdTRU1hdHJpeD1GQUxTRSkKY3VycmVudF9ncGwgPC0gbmFtZXMoR1BMTGlzdChnc2UpKVsxXQpjdXJyZW50X2dwbF9pbmZvIDwtIE1ldGEoZ2V0R0VPKGN1cnJlbnRfZ3BsKSkKYGBgCgoqKlBsYXRmb3JtIHRpdGxlOioqIGByIGN1cnJlbnRfZ3BsX2luZm8kdGl0bGVgCgoqKlN1Ym1pc3Npb24gZGF0ZSoqIGByIGN1cnJlbnRfZ3BsX2luZm8kc3VibWlzc2lvbl9kYXRlYAoKKipMYXN0IHVwZGF0ZSBkYXRlOioqIGByIGN1cnJlbnRfZ3BsX2luZm8kbGFzdF91cGRhdGVfZGF0ZWAKCioqT3JnYW5pc21zOioqICBgciBjdXJyZW50X2dwbF9pbmZvJG9yZ2FuaXNtYAoKVG8gc3VtbWFyaXplIHRoZSBwYXBlciwgdGhleSBjb2xsZWN0ZWQgd2lsZCB0eXBlIGN1bHR1cmVkIGh1bWFuIG5ldXJvbnMgYW5kIGVkaXRlZCBjZWxsIGxpbmVzIHRoYXQga25vY2tlZCBvdXQgdGhlIEFTWEwxIGdlbmUuIFRoZSBrbm9ja291dCB3YXMgc2hvd24gdG8gaW50ZXJmZXJlIHdpdGggbmV1cmFsIGNyZXN0IGZvcm1hdGlvbi4gUGFydCBvZiB0aGUgZXhwZXJpbWVudCB3YXMgZ2xvYmFsIFJOQSBzZXF1ZW5jaW5nIHRvIGZpbmQgd2hhdCBjaGFuZ2VzIGluIGV4cHJlc3Npb24gb2NjdXIgYWZ0ZXIgQVNYTDEgaXMga25vY2tlZCBvdXQuCgpOb3RlIHRoYXQgaW5zaWRlIHRoZSBwYXBlciwgY2hpY2tlbiBlbWJyeW9zIHdlcmUgdXNlZCBhdCB2YXJpb3VzIHN0YWdlcyBvZiBleHBlcmltZW50YXRpb24gdG8gZGVtb25zdHJhdGUgbWlncmF0aW9uLiBUaGlzIGRhdGFzZXQsIGhvd2V2ZXIsIG9ubHkgY29udGFpbnMgcmF3IGNvdW50cyBmcm9tIGhpZ2gtdGhvcm91Z2hwdXQgUk5BIHNlcXVlbmNpbmcgb2YgY3VsdHVyZWQgaHVtYW4gbmV1cm9ucy4KClVzaW5nIHRoZSBjb2RlIGZyb20gc2xpZGUgNTYgb2YgTGVjdHVyZSAzOgpgYGB7Un0Kc2ZpbGVzIDwtIGdldEdFT1N1cHBGaWxlcygnR1NFMTIwMjAwJykKY291bnRzIDwtIHJlYWQuZGVsaW0ocm93bmFtZXMoc2ZpbGVzKVsxXSxoZWFkZXI9VFJVRSwgY2hlY2submFtZXMgPSBGQUxTRSkKY291bnRzX21hdHJpeCA8LSBjb3VudHNbLC0xXQpyb3duYW1lcyhjb3VudHNfbWF0cml4KSA8LSBjb3VudHNbLDFdCmhlYWQoY291bnRzKQpoZWFkKGNvdW50c19tYXRyaXgpCmBgYAojIyMgMi0gb3ZlcnZpZXcgc3RhdGlzdGljcwoKYGBge1J9CiNhcmUgdGhlcmUgYW55IGR1cGxpY2F0ZXM/IENvZGUgYWRhcHRlZCBmcm9tIExlY3R1cmUgNCBzbGlkZSAyMwpzdW1tYXJpemVkX2dlbmVfY291bnRzIDwtIHNvcnQodGFibGUoY291bnRzJEdlbmVpZCksZGVjcmVhc2luZyA9IFRSVUUpCmxlbmd0aChzdW1tYXJpemVkX2dlbmVfY291bnRzW3doaWNoKHN1bW1hcml6ZWRfZ2VuZV9jb3VudHM+MSldKQoKYGBgClRoaXMgbWVhbnMgYWxsIHRoZSBlbnNlbWJsIElEcyBhcmUgaWRlbnRpY2FsLgoKSG93IG1hbnkgZ2VuZXMgYXJlIHRoZXJlPwpgYGB7Un0KZGltKGNvdW50cykKCmBgYApBcmUgdGhleSBhbGwgZ2VuZXM/CmBgYHtSfQp0YWlsKGNvdW50cykKCmBgYApObywgRVJDQyBpc24ndCBhbiBFbnNlbWJsIGdlbmUgSUQuCgpTbyB3aGF0IGlzIEVSQ0M/CltJdCBzZWVtcyBsaWtlIGEgcXVhbGl0eSBjb250cm9sLl0oaHR0cHM6Ly93d3cubmlzdC5nb3YvcHJvZ3JhbXMtcHJvamVjdHMvZXh0ZXJuYWwtcm5hLWNvbnRyb2xzLWNvbnNvcnRpdW0pCgpgYGB7Un0KI0RpZCB3ZSBnZXQgYW55IHJlYWRzIGluIHRoZSBjb250cm9scz8Kc3VtKHJvd1N1bXMoY291bnRzW2dyZXAoIkVSQ0MiLCBjb3VudHMkR2VuZWlkKSwtMV0pKQoKYGBgCkl0IGxvb2tzIGxpa2UgYWxsIG9mIHRoZW0gd2lsbCBiZSBmaWx0ZXJlZCBvdXQgZm9yIGhhdmluZyBhIGxvdyBjb3VudCBvZiAwLCBzbyB0aGV5IGNhbiBiZSBpZ25vcmVkIHNhZmVseS4KVGhpcyBhbHNvIHNob3dzIHRoYXQgdGhlcmUgd2Fzbid0IGFueSBleHRlcm5hbCBjb250YW1pbmF0aW9uIGZvdW5kIHVzaW5nIHRoZSBFUkNDIGtpdC4KCkZpbHRlcmluZyBvdXQgbG93IGNvdW50cyB1c2luZyBjb2RlIGFkYXB0ZWQgZnJvbSBMZWN0dXJlIDQgU2xpZGUgMjU6CmBgYHtSfQpjcG1zIDwtIGNwbShjb3VudHNbLC0xXSkKcm93bmFtZXMoY3BtcykgPC0gY291bnRzWywxXQoKI1dlIGhhdmUgMyAnbm9ybWFsJyBzYW1wbGVzIGFuZCA3ICdkaXNlYXNlZCcgc2FtcGxlcyBzbyBsZXQncyB1c2UgMywgc2luY2UgdGhhdCBpcyB0aGUgc21hbGxlc3QgZ3JvdXAgb2YgcmVwbGljYXRlcy4Ka2VlcCA8LSByb3dTdW1zKGNwbXMgPiAxKSA+PSAzCmNvdW50c19maWx0ZXJlZCA8LSBjb3VudHNba2VlcCxdCiNkZWxldGUgYXV0b2dlbmVyYXRlZCByb3duYW1lcyBsZWZ0IG92ZXIgZnJvbSBjb3VudHMKcm93bmFtZXMoY291bnRzX2ZpbHRlcmVkKSA9IGMoKQpkaW0oY291bnRzX2ZpbHRlcmVkKQpgYGAKflR3byB0aGlyZHMgb2YgdGhlIGdlbmUgaWRzIHdlcmUgcmVtb3ZlZCB3aXRoIHRoaXMgZmlsdGVyaW5nIHN0ZXAuCgpOb3cgdG8gbWFrZSBncmFwaHMuIEFkYXB0aW5nIHRoZSBjb2RlIGZyb20gU2xpZGUgMzYgaW4gTGVjdHVyZSA0OgpgYGB7Un0KI0JveHBsb3Q6CmJveHBsb3RfZGF0YSA8LSBsb2cyKGNwbShjb3VudHNfZmlsdGVyZWRbLC0xXSkpCnJvd25hbWVzKGJveHBsb3RfZGF0YSkgPSBjb3VudHNfZmlsdGVyZWRbLDFdCmJveHBsb3QoYm94cGxvdF9kYXRhLCB4bGFiID0gIlNhbXBsZXMiLCB5bGFiID0gImxvZzIgQ1BNIiwgCiAgICAgICAgbGFzID0gMiwgY2V4ID0gMC41LCBjZXgubGFiID0gMC41LAogICAgICAgIGNleC5heGlzID0gMC41LCBtYWluID0gIkFTWEwxIFJOQVNlcSBTYW1wbGVzIikKYWJsaW5lKGggPSBtZWRpYW4oYXBwbHkoYm94cGxvdF9kYXRhLCAyLCBtZWRpYW4pKSwgY29sID0gIm9yYW5nZXJlZCIsIGx3ZCA9IDAuNiwgbHR5ID0gImRhc2hlZCIpCmBgYApTdHJhbmdlIHRoYXQgdGhlcmUgYXJlIG91dGxpZXJzIG9mIC1pbmYuIFRoaXMgbWVhbnMgdGhhdCB0aGVyZSBoYXMgdG8gYmUgMCB2YWx1ZXMgaW4gdGhlIGRhdGFzZXQgYWZ0ZXIgZmlsdGVyaW5nIHN0ZXAuCgpgYGB7Un0KbWluKGJveHBsb3RfZGF0YSkKbWluKGNvdW50c19maWx0ZXJlZFssLTFdKQpgYGAKSVQgdHVybnMgb3V0IHRoYXQgd2UgZGlkbid0IGZpbHRlciBvdXQgYWxsIGxvdyBleHByZXNzaW9ucy4gVGhlcmUgYXJlIDBzIGluIHNvbWUgYnV0IG5vdCBhbGwgb2YgdGhlIGV4cGVyaW1lbnRzIGZvciBhIHBhcnRpY3VsYXIgZ2VuZS4gSXQgaXMgcHJvYmFibHkgaW5jb25zZXF1ZW50aWFsLgoKRGVuc2l0eSBwbG90OiAoVXNpbmcgY29kZSBhZGFwdGVkIGZyb20gTGVjdHVyZSA0IHNsaWRlIDM4KQpgYGB7Un0KY291bnRzX2RlbnNpdHkgPC0gYXBwbHkobG9nMihjcG0oY291bnRzX2ZpbHRlcmVkWywtMV0pKSwgMiwgZGVuc2l0eSkKCnhsaW0gPC0gMDsgeWxpbSA8LSAwCiAgICBmb3IgKGkgaW4gMTpsZW5ndGgoY291bnRzX2RlbnNpdHkpKSB7CiAgICAgIHhsaW0gPC0gcmFuZ2UoYyh4bGltLCBjb3VudHNfZGVuc2l0eVtbaV1dJHgpKTsgCiAgICAgIHlsaW0gPC0gcmFuZ2UoYyh5bGltLCBjb3VudHNfZGVuc2l0eVtbaV1dJHkpKQogICAgfQpjb2xzIDwtIHJhaW5ib3cobGVuZ3RoKGNvdW50c19kZW5zaXR5KSkKICAgIAogICAgbHR5cyA8LSByZXAoMSwgbGVuZ3RoKGNvdW50c19kZW5zaXR5KSkKcGxvdChjb3VudHNfZGVuc2l0eVtbMV1dLCB4bGltPXhsaW0sIHlsaW09eWxpbSwgdHlwZT0ibiIsIAogICAgICAgICB5bGFiPSJTbW9vdGhpbmcgZGVuc2l0eSBvZiBsb2cyLUNQTSIsIG1haW49IiIsIGNleC5sYWIgPSAwLjg1KQogICAgI3Bsb3QgZWFjaCBsaW5lCiAgICBmb3IgKGkgaW4gMTpsZW5ndGgoY291bnRzX2RlbnNpdHkpKSBsaW5lcyhjb3VudHNfZGVuc2l0eVtbaV1dLCBjb2w9Y29sc1tpXSwgbHR5PWx0eXNbaV0pCiAgICAjY3JlYXRlIGxlZ2VuZAogICAgbGVnZW5kKCJ0b3ByaWdodCIsIGNvbG5hbWVzKGJveHBsb3RfZGF0YSksICAKICAgICAgICAgICBjb2w9Y29scywgbHR5PWx0eXMsIGNleD0wLjc1LCAKICAgICAgICAgICBib3JkZXIgPSJyZWQiLCAgdGV4dC5jb2wgPSAiZ3JlZW40IiwgCiAgICAgICAgICAgbWVyZ2UgPSBUUlVFLCBiZyA9ICJncmF5ODAiKQpgYGAKVGhlIGRpZmZlcmVudCBzYW1wbGVzIGxvb2sgcHJldHR5IGNsb3NlIHRvIGVhY2ggb3RoZXIsIHdpdGggdGhlIGV4Y2VwdGlvbiBvZiBXVDIsIGJ1dCBldmVuIHRoYXQgaXMgc3RpbGwgYSByZWFzb25hYmxlIGRldmlhdGlvbiBpbiBzaGFwZS4KCiMjIyAzLSBjb252ZXJ0IHRvIEhVR08gR2VuZSBOb21lbmNsYXR1cmUgQ29tbWl0dGVlIHN5bWJvbHMKYGBge1J9CmNvbnZlcnNpb25fc3Rhc2ggPC0gImRhdGFfaWRfY29udmVyc2lvbi5yZHMiCmlmKGZpbGUuZXhpc3RzKGNvbnZlcnNpb25fc3Rhc2gpKXsKICBkYXRhX2lkX2NvbnZlcnNpb24gPC0gcmVhZFJEUyhjb252ZXJzaW9uX3N0YXNoKQp9IGVsc2UgewogIGVuc2VtYmwgPC0gdXNlTWFydCgiZW5zZW1ibCIpCiAgZGF0YV9pZF9jb252ZXJzaW9uIDwtIGdldEJNKGF0dHJpYnV0ZXMgPSBjKCJlbnNlbWJsX2dlbmVfaWQiLCJoZ25jX3N5bWJvbCIpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgZmlsdGVycyA9IGMoImVuc2VtYmxfZ2VuZV9pZCIpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgdmFsdWVzID0gY291bnRzJEdlbmVpZCwgI3VzZSBJRHMgZnJvbSBiZWZvcmUgdGhlIGZpbHRlcmluZyBzdGFnZQogICAgICAgICAgICAgICAgICAgICAgICAgICAgbWFydCA9IGVuc2VtYmwpCiAgc2F2ZVJEUyhkYXRhX2lkX2NvbnZlcnNpb24sIGNvbnZlcnNpb25fc3Rhc2gpCn0KaGVhZChkYXRhX2lkX2NvbnZlcnNpb24pCmBgYAoKKipJZiB0aGUgZW5zZW1ibCBzZXJ2ZXJzIGFyZSBub25vcGVyYXRpb25hbCBJIGhhdmUgaW5jbHVkZWQgYSBmaWxlIGNhbGxlZCAiZGF0YV9pZF9jb252ZXJzaW9uX2JhY2t1cC5yZHMiIHdoaWNoIGNhbiBiZSByZW5hbWVkIHRvICJkYXRhX2lkX2NvbnZlcnNpb24ucmRzIiB0byBiZSBhYmxlIHRvIHByb2NlZWQuKioKCmBgYHtSfQojRHVwbGljYXRlIGhnbmMgc3ltYm9scz8Kc3VtbWFyaXplZF9oZ25jX2NvdW50cyA8LSBzb3J0KHRhYmxlKGRhdGFfaWRfY29udmVyc2lvbiRoZ25jX3N5bWJvbCksZGVjcmVhc2luZyA9IFRSVUUpCnN1bW1hcml6ZWRfaGduY19jb3VudHNbd2hpY2goc3VtbWFyaXplZF9oZ25jX2NvdW50cz4xKVsxOjEwXV0KZGltKHN1bW1hcml6ZWRfaGduY19jb3VudHNbd2hpY2goc3VtbWFyaXplZF9oZ25jX2NvdW50cz4xKV0pCgojRHVwbGljYXRlIGVuc2VtYmxlIGlkcz8Kc3VtbWFyaXplZF9lbnNlbWJsZV9jb3VudHMgPC0gc29ydCh0YWJsZShkYXRhX2lkX2NvbnZlcnNpb24kZW5zZW1ibF9nZW5lX2lkKSxkZWNyZWFzaW5nID0gVFJVRSkKc3VtbWFyaXplZF9lbnNlbWJsZV9jb3VudHNbd2hpY2goc3VtbWFyaXplZF9lbnNlbWJsZV9jb3VudHM+MSlbMToyXV0KZGltKHN1bW1hcml6ZWRfZW5zZW1ibGVfY291bnRzW3doaWNoKHN1bW1hcml6ZWRfZW5zZW1ibGVfY291bnRzPjEpXSkKCmBgYApUaGVyZSBhcmUgYHIgZGltKHN1bW1hcml6ZWRfaGduY19jb3VudHNbd2hpY2goc3VtbWFyaXplZF9oZ25jX2NvdW50cz4xKV0pYCBkdXBsaWNhdGVkIGhnbmMgc3ltYm9scyB3aGVuIG1hcHBlZCBmcm9tIGVuc2VtYmwgZ2VuZSBpZHMuCgpBY2NvcmRpbmcgdG8gdGhlIFtFbnNlbWJsIHdlYnNpdGVdKGh0dHBzOi8vdXNlYXN0LmVuc2VtYmwub3JnL0hvbW9fc2FwaWVucy9HZW5lL1N1bW1hcnk/Zz1FTlNHMDAwMDAyMjU4NDU7cj1DSFJfSFNDSFI2X01IQ19NQ0ZfQ1RHMTozMjQ3MDEyMC0zMjQ4MzI5MikgYXMgd2VsbCBhcyB0aGUgW0hHTkMgd2Vic2l0ZV0oaHR0cHM6Ly93d3cuZ2VuZW5hbWVzLm9yZy9kYXRhL2dlbmUtc3ltYm9sLXJlcG9ydC8jIS9oZ25jX2lkL0hHTkM6MTE0MiksIGF0IGxlYXN0IGZvciBCVE5MMiwgdGhlcmUgd2VyZSBtdWx0aXBsZSBzcGxpY2VzIGZvciBpdCB0aGF0IGNvcnJlc3BvbmRlZCB0byBhbGwgdGhlIGRpZmZlcmVudCBFbnNlbWJsIGdlbmUgaWRzLiAKCkluIHN1Y2ggYSBjYXNlLCBhY2NvcmRpbmcgdG8gTWFjRG9uYWxkICgyMDE2KSBvbiBbQmlvY29uZHVjdG9yIEZvcnVtc10oaHR0cHM6Ly9zdXBwb3J0LmJpb2NvbmR1Y3Rvci5vcmcvcC85MDE4NS8pIGlmIHRoZSB0cmFuc2NyaXB0IGxlbmd0aHMgd2VyZSBzaWduaWZpY2FudGx5IGRpZmZlcmVudCwgc3VtbWluZyB1cCBhbGwgdGhlIHJlYWRzIHdvdWxkbid0IG5lY2Vzc2FyaWx5IGJlIHRoZSBjb3JyZWN0IGRlY2lzaW9uLiBUaGVyZSBpcyBhIGdvb2QgY2hhbmNlLCBob3dldmVyLCB0aGF0IHRoZSBnZW5lcyBpbiBxdWVzdGlvbiBkbyBub3QgbmVjZXNzYXJpbHkgaGF2ZSByZWFkcywgYW5kIGluIHRoZSBzdGVwIHdoZXJlIHdlIHJlbW92ZSBhbGwgdGhlIGVudHJpZXMgdGhhdCBoYXZlIGxvdyBjb3VudHMgdGhleSBtaWdodCBub3QgZXhpc3QgYW55bW9yZS4gTm90ZSB0aGF0IHdlIG1hZGUgdGhlIGdlbmUgaWQgbWFwcGluZyB1c2luZyB0aGUgaWRzIGJlZm9yZSB0aGUgZmlsdGVyaW5nIHN0ZXAuCgpFc3NlbnRpYWxseSBsaWtlIGluIHNsaWRlIDI0IG9mIExlY3R1cmUgNCwgdGhlIHByb2JsZW0gaXMgaWdub3JlZCBpbiB0aGUgaG9wZXMgdGhhdCBpdCBkb2Vzbid0IGFjdHVhbGx5IGhhdmUgdG8gYmUgZGVhbHQgd2l0aC4KClRoZXJlIGFyZSBhbHNvIGByIGRpbShzdW1tYXJpemVkX2hnbmNfY291bnRzW3doaWNoKHN1bW1hcml6ZWRfZW5zZW1ibGVfY291bnRzPjEpXSlgIGR1cGxpY2F0ZWQgZW5zZW1ibCBpZHMgaW4gdGhlIG1hcHBpbmcuCgpTaW5jZSB0aGlzIGlzbid0IGEgaGlnaCBudW1iZXIsIHdlIGNhbiBsb29rIGF0IHRoZW0gbWFudWFsbHkgYW5kIHNlZSB3aGF0IGhhcHBlbmVkOgpgYGB7Un0KZHVwcyA8LSB3aGljaChzdW1tYXJpemVkX2Vuc2VtYmxlX2NvdW50cz4xKQpkYXRhX2lkX2NvbnZlcnNpb25bbmFtZXMoZHVwcylbMV0gPT0gZGF0YV9pZF9jb252ZXJzaW9uJGVuc2VtYmxfZ2VuZV9pZCB8IG5hbWVzKGR1cHMpWzJdID09IGRhdGFfaWRfY29udmVyc2lvbiRlbnNlbWJsX2dlbmVfaWQsXQpgYGAKR29vZ2xpbmcgQzEyb3JmNzQsIGl0IHNlZW1zIHRvIGJlIGFuIG9sZGVyIHN5bWJvbC4gSSBwcmVzdW1lIHRoZSBsYXRlciBlbnRyeSBmb3IgYm90aCBvZiB0aGVzZSBkdXBsaWNhdGVkIElEcyBpcyB0aGUgbmV3ZXIgc3ltYm9sLgoKIyMjIDQgLSBOb3JtYWxpemF0aW9uCgpBcHBseSBUTU0gYnkgdXNpbmcgZWRnZVIuIFRNTSB3YXMgcGlja2VkIGFyYml0cmFyaWx5IGJlY2F1c2UgaXQgd2FzIHdoYXQgd2FzIHVzZWQgaW4gbGVjdHVyZSwgaXQgaXMgc3BlY2lhbGl6ZWQgZm9yIFJOQS1zZXEsIGFuZCBpbiBhZGRpdGlvbiwgaXQgaXMgd2hhdCBJIHVzdWFsbHkgaGVhciBhYm91dCBpbiBkaXNjdXNzaW9ucyBvZiBub3JtYWxpemF0aW9uLgoKYGBge1J9CmNvdW50c19maWx0ZXJlZF9tYXRyaXggPC0gYXMubWF0cml4KGNvdW50c19maWx0ZXJlZFssLTFdKQpyb3duYW1lcyhjb3VudHNfZmlsdGVyZWRfbWF0cml4KSA8LSBjb3VudHNfZmlsdGVyZWQkR2VuZWlkCnNhbXBsZXMgPC0gYyhyZXAoIldUIiwgMyksIHJlcCAoIkhPTSIsIDcpKQpkID0gREdFTGlzdChjb3VudHM9Y291bnRzX2ZpbHRlcmVkX21hdHJpeCwgZ3JvdXA9c2FtcGxlcykKZCA9IGNhbGNOb3JtRmFjdG9ycyhkKQpub3JtYWxpemVkX2ZpbHRlcmVkX2NvdW50cyA8LSBjcG0oZCkKCm5vcm1hbGl6ZWRfY291bnRzX2RlbnNpdHkgPC0gYXBwbHkobG9nMihub3JtYWxpemVkX2ZpbHRlcmVkX2NvdW50cyksIDIsIGRlbnNpdHkpCgp4bGltIDwtIDA7IHlsaW0gPC0gMAogICAgZm9yIChpIGluIDE6bGVuZ3RoKG5vcm1hbGl6ZWRfY291bnRzX2RlbnNpdHkpKSB7CiAgICAgIHhsaW0gPC0gcmFuZ2UoYyh4bGltLCBub3JtYWxpemVkX2NvdW50c19kZW5zaXR5W1tpXV0keCkpOyAKICAgICAgeWxpbSA8LSByYW5nZShjKHlsaW0sIG5vcm1hbGl6ZWRfY291bnRzX2RlbnNpdHlbW2ldXSR5KSkKICAgIH0KY29scyA8LSByYWluYm93KGxlbmd0aChub3JtYWxpemVkX2NvdW50c19kZW5zaXR5KSkKICAgIAogICAgbHR5cyA8LSByZXAoMSwgbGVuZ3RoKG5vcm1hbGl6ZWRfY291bnRzX2RlbnNpdHkpKQoKcGFyKG1mcm93PWMoMiwyKSkKCnBsb3Qobm9ybWFsaXplZF9jb3VudHNfZGVuc2l0eVtbMV1dLCB4bGltPXhsaW0sIHlsaW09eWxpbSwgdHlwZT0ibiIsIAogICAgICAgICB5bGFiPSJTbW9vdGhpbmcgZGVuc2l0eSBvZiBsb2cyLUNQTSIsIG1haW49Ik5vcm1hbGl6ZWQiLCBjZXgubGFiID0gMC44NSkKICAgICNwbG90IGVhY2ggbGluZQogICAgZm9yIChpIGluIDE6bGVuZ3RoKG5vcm1hbGl6ZWRfY291bnRzX2RlbnNpdHkpKSBsaW5lcyhub3JtYWxpemVkX2NvdW50c19kZW5zaXR5W1tpXV0sIGNvbD1jb2xzW2ldLCBsdHk9bHR5c1tpXSkKICAgICNjcmVhdGUgbGVnZW5kCiAgICBsZWdlbmQoInRvcHJpZ2h0IiwgY29sbmFtZXMoYm94cGxvdF9kYXRhKSwgIAogICAgICAgICAgIGNvbD1jb2xzLCBsdHk9bHR5cywgY2V4PTAuNzUsIAogICAgICAgICAgIGJvcmRlciA9InJlZCIsICB0ZXh0LmNvbCA9ICJncmVlbjQiLCAKICAgICAgICAgICBtZXJnZSA9IFRSVUUsIGJnID0gImdyYXk4MCIpCgojY29tcGFyZSB0byBvbGQgcGxvdApwbG90KGNvdW50c19kZW5zaXR5W1sxXV0sIHhsaW09eGxpbSwgeWxpbT15bGltLCB0eXBlPSJuIiwgCiAgICAgICAgIHlsYWI9IlNtb290aGluZyBkZW5zaXR5IG9mIGxvZzItQ1BNIiwgbWFpbj0iTm90IE5vcm1hbGl6ZWQiLCBjZXgubGFiID0gMC44NSkKICAgICNwbG90IGVhY2ggbGluZQogICAgZm9yIChpIGluIDE6bGVuZ3RoKGNvdW50c19kZW5zaXR5KSkgbGluZXMoY291bnRzX2RlbnNpdHlbW2ldXSwgY29sPWNvbHNbaV0sIGx0eT1sdHlzW2ldKQogICAgI2NyZWF0ZSBsZWdlbmQKICAgIGxlZ2VuZCgidG9wcmlnaHQiLCBjb2xuYW1lcyhib3hwbG90X2RhdGEpLCAgCiAgICAgICAgICAgY29sPWNvbHMsIGx0eT1sdHlzLCBjZXg9MC43NSwgCiAgICAgICAgICAgYm9yZGVyID0icmVkIiwgIHRleHQuY29sID0gImdyZWVuNCIsIAogICAgICAgICAgIG1lcmdlID0gVFJVRSwgYmcgPSAiZ3JheTgwIikKICAgIAogICAgCiNib3hwbG90cwogICAgI0JveHBsb3Q6CmJveHBsb3RfZGF0YV9ub3JtYWxpemVkIDwtIGxvZzIobm9ybWFsaXplZF9maWx0ZXJlZF9jb3VudHMpCmJveHBsb3QoYm94cGxvdF9kYXRhX25vcm1hbGl6ZWQsIHhsYWIgPSAiU2FtcGxlcyIsIHlsYWIgPSAibG9nMiBDUE0iLCAKICAgICAgICBsYXMgPSAyLCBjZXggPSAwLjUsIGNleC5sYWIgPSAwLjUsCiAgICAgICAgY2V4LmF4aXMgPSAwLjUsIG1haW4gPSAiTm9ybWFsaXplZCIpCmFibGluZShoID0gbWVkaWFuKGFwcGx5KGJveHBsb3RfZGF0YV9ub3JtYWxpemVkLCAyLCBtZWRpYW4pKSwgY29sID0gIm9yYW5nZXJlZCIsIGx3ZCA9IDAuNiwgbHR5ID0gImRhc2hlZCIpCgojb2xkIGJveHBsb3QKYm94cGxvdChib3hwbG90X2RhdGEsIHhsYWIgPSAiU2FtcGxlcyIsIHlsYWIgPSAibG9nMiBDUE0iLCAKICAgICAgICBsYXMgPSAyLCBjZXggPSAwLjUsIGNleC5sYWIgPSAwLjUsCiAgICAgICAgY2V4LmF4aXMgPSAwLjUsIG1haW4gPSAiTm90IE5vcm1hbGl6ZWQiKQphYmxpbmUoaCA9IG1lZGlhbihhcHBseShib3hwbG90X2RhdGEsIDIsIG1lZGlhbikpLCBjb2wgPSAib3JhbmdlcmVkIiwgbHdkID0gMC42LCBsdHkgPSAiZGFzaGVkIikKYGBgCkxvb2tpbmcgYXQgdGhlc2UgcGxvdHMsIHRoZXJlIGlzbid0IG11Y2gsIGlmIGFueSBkaWZmZXJlbmNlLiBOb3RoaW5nIHN0cmlraW5nIGxpa2Ugd2hhdCB3YXMgcHJlc2VudGVkIGluIGxlY3R1cmUuCgpgYGB7Un0KCnBsb3RNRFMoZCwgbGFiZWxzPXJvd25hbWVzKHNhbXBsZXMpLAogIGNvbCA9IGMoImRhcmtncmVlbiIsImJsdWUiKVtmYWN0b3Ioc2FtcGxlcyldKQoKbW9kZWxfZGVzaWduIDwtIG1vZGVsLm1hdHJpeCh+c2FtcGxlcykKCmQgPC0gZXN0aW1hdGVEaXNwKGQsIG1vZGVsX2Rlc2lnbikKCnBhcihtZnJvdz1jKDEsMikpCgpwbG90QkNWKGQsY29sLnRhZ3dpc2UgPSAiYmxhY2siLGNvbC5jb21tb24gPSAicmVkIikKCnBsb3RNZWFuVmFyKGQsIHNob3cucmF3LnZhcnMgPSBUUlVFLAogICAgICAgICAgICBzaG93LnRhZ3dpc2UudmFycz1UUlVFLCBOQmxpbmU9VFJVRSwgCiAgICAgICAgICAgIHNob3cuYXZlLnJhdy52YXJzID0gVFJVRSxzaG93LmJpbm5lZC5jb21tb24uZGlzcC52YXJzID0gVFJVRSkKYGBgCgpUaGUgTURTIHBsb3QgbG9va3MgZ29vZCwgV1QgYW5kIEhPTSBhcmUgd2VsbCBzZXBhcmF0ZWQgaW50byBkaWZmZXJlbnQgcXVhZHJhbnRzLgpUaGUgQkNWIHBsb3QgYW5kIG1lYW4tdmFyaWFuY2UgcGxvdCBsb29rIHNpbWlsYXIgdG8gdGhlIG9uZXMgaW4gdGhlIHNsaWRlcywgc28gdGhleSBhcmUgcHJvYmFibHkgb2sgYXMgd2VsbC4KCiMjIyA1IC0gQXBwbHkgaWRlbnRpZmllciBtYXBwaW5nCgpgYGB7Un0Kbm9ybWFsaXplZF9maWx0ZXJlZF9jb3VudHNfYW5ub3QgPC0gbWVyZ2UoZGF0YV9pZF9jb252ZXJzaW9uLCBub3JtYWxpemVkX2ZpbHRlcmVkX2NvdW50cywgYnkueD0xLCBieS55PTAsIGFsbC55PVRSVUUpCiNmaW5hbF9kYXRhX2ZyYW1lIDwtIGFzLmRhdGEuZnJhbWUobm9ybWFsaXplZF9maWx0ZXJlZF9jb3VudHMpCgplbnNlbWJsX2lkX21pc3NpbmdfZ2VuZSA8LSBub3JtYWxpemVkX2ZpbHRlcmVkX2NvdW50c19hbm5vdCRlbnNlbWJsX2dlbmVfaWRbCiAgd2hpY2goaXMubmEobm9ybWFsaXplZF9maWx0ZXJlZF9jb3VudHNfYW5ub3QkaGduY19zeW1ib2wpIHwgbm9ybWFsaXplZF9maWx0ZXJlZF9jb3VudHNfYW5ub3QkaGduY19zeW1ib2wgPT0gJycpXQojSG93IG1hbnkgZW5zZW1ibCBpZHMgZG9uJ3QgaGF2ZSBzeW1ib2xzPwpsZW5ndGgoZW5zZW1ibF9pZF9taXNzaW5nX2dlbmUpCgpoZ25jX3RhYmxlIDwtIG5vcm1hbGl6ZWRfZmlsdGVyZWRfY291bnRzX2Fubm90W3doaWNoKCEobm9ybWFsaXplZF9maWx0ZXJlZF9jb3VudHNfYW5ub3QkZW5zZW1ibF9nZW5lX2lkICVpbiUgZW5zZW1ibF9pZF9taXNzaW5nX2dlbmUpKSxdCgojYW55IE5BcyBsZWZ0Pwp3aGljaChpcy5uYShoZ25jX3RhYmxlJGhnbmNfc3ltYm9sKSkKCiNhbnkgZHVwbGljYXRlcyBpbiBlbnNlbWJsIGdlbmUgaWRzPwpkdXBsaWNhdGVkKGhnbmNfdGFibGUkZW5zZW1ibGVfZ2VuZV9pZCkKCiNhbnkgZHVwbGljYXRlcyBpbiBoZ25jIGlkcz8KbGVuZ3RoKHdoaWNoKGR1cGxpY2F0ZWQoaGduY190YWJsZSRoZ25jX3N5bWJvbCkpKQoKI2lmIHRoZXJlIHdlcmUgZHVwbGljYXRlcywgdGhpcyBpcyBob3cgSSB3b3VsZCd2ZSBzdW1tZWQgdGhlbSB1cCwgYWdhaW5zdCB0aGUgYWR2aWNlIG9mIE1hY0RvbmFsZDoKI3VuaXF1ZV9oZ25jIDwtIGFnZ3JlZ2F0ZShoZ25jX3RhYmxlWywzOjEyXSwgbGlzdChoZ25jX3RhYmxlWywyXSksIEZVTj0ic3VtIikKCmBgYAoKVGhlIGlkZWEgdG8gdXNlIHRoZSBhZ2dyZWdhdGUgZnVuY3Rpb24gY2FtZSBmcm9tICJKaW1ib3UiICgyMDE1KSBhbnN3ZXJpbmcgYSBxdWVzdGlvbiBvbiBbQmlvc3RhcnNdKGh0dHBzOi8vd3d3LmJpb3N0YXJzLm9yZy9wLzE2NzAyOC8pLgoKVGhlcmUgYXJlIGByIGxlbmd0aCh3aGljaChkdXBsaWNhdGVkKGhnbmNfdGFibGUkaGduY19zeW1ib2wpKSlgIGR1cGxpY2F0ZXMgb2YgSEdOQyBzeW1ib2xzIHRoYXQgd2UgbmVlZCB0byByZXNvbHZlLgpMdWNreSB1cyEKCk5vdyB0aGF0IGFsbCBvdXIgd29ycmllcyBoYXZlIGJlZW4gbWlyYWN1bG91c2x5IHNvbHZlZCBieSB0aGUgZmlsdGVyaW5nIHN0ZXAgZXhjZXB0IGZvciBgciBsZW5ndGgoZW5zZW1ibF9pZF9taXNzaW5nX2dlbmUpICogMTAwIC8gbnJvdyhub3JtYWxpemVkX2ZpbHRlcmVkX2NvdW50cylgJSBvZiB0aGUgZGF0YXNldCBiZWluZyByZW1vdmVkIGR1ZSB0byBsYWNrIG9mIEhHTkMgc3ltYm9scywgd2UgY2FuIGNyZWF0ZSB0aGUgZmluYWwgZGF0YSBmcmFtZS4KCmBgYHtSfQpmaW5hbF9hMV9kYXRhID0gYXMuZGF0YS5mcmFtZShoZ25jX3RhYmxlWzM6MTJdKQpyb3duYW1lcyhmaW5hbF9hMV9kYXRhKSA8LSBoZ25jX3RhYmxlWywyXQpzYW1wbGUoZmluYWxfYTFfZGF0YSwgMTApCmBgYAojIyMgRmluYWwgcXVlc3Rpb25zCgpJIGFscmVhZHkgYW5zd2VyZWQgc29tZSBvZiB0aGVzZSBxdWVzdGlvbnMgYWJvdmUsIGJ1dCBJIHdpbGwgZm9ybWFsaXplIHRoZW0gaGVyZSBhbnl3YXlzLgoKKiBXaGF0IGFyZSB0aGUgY29udHJvbCBhbmQgdGVzdCBjb25kaXRpb25zIG9mIHRoZSBkYXRhc2V0PwoKICAgIENvbnRyb2wgY29uZGl0aW9uOiB3aWxkIHR5cGUgQVNYTDEgZ2VuZSBpbiBodW1hbiBpbmR1Y2VkIHBsdXJpcG90ZW50IHN0ZW0gY2VsbCBkaWZmZXJlbnRpYXRlZCBuZXVyYWwgY3Jlc3QgcHJvZ2VuaXRvcnMuIFRlc3QgY29uZGl0aW9uOiBDUklTUFIvQ2FzOSBlZGl0ZWQgQVNYTDEgZ2VuZSAoa25vY2tvdXQpIGluIGh1bWFuIGVtYnJ5b25pYyBzdGVtIGNlbGwgZGlmZmVyZW50aWF0ZWQgbmV1cmFsIGNyZXN0IHByb2dlbml0b3JzLgoKKiBXaHkgaXMgdGhlIGRhdGFzZXQgb2YgaW50ZXJlc3QgdG8geW91PwoKICAgIFRoZSBkYXRhc2V0IGlzIG9mIGludGVyZXN0IGJlY2F1c2UgdGhlIG1lY2hhbmlzbSBvZiBuZXVyYWwgY2VsbCBtaWdyYXRpb24gaXMgaW50ZXJlc3RpbmcgdG8gbWUuIFVuZGVyc3RhbmRpbmcgdGhpcyBtZWNoYW5pc20gaGFzIGltcGxpY2F0aW9ucyBmb3IgYnJhaW4gZGV2ZWxvcG1lbnQgYW5kIHRoZXJlZm9yZSB0aGUgYmFzaXMgb2YgaHVtYW4gaW50ZWxsaWdlbmNlLgoKKiBXZXJlIHRoZXJlIGV4cHJlc3Npb24gdmFsdWVzIHRoYXQgd2VyZSBub3QgdW5pcXVlIGZvciBzcGVjaWZpYyBnZW5lcz8gSG93IGRpZCB5b3UgaGFuZGxlIHRoZXNlPwoKICAgIEFmdGVyIHRoZSBmaWx0ZXJpbmcgc3RlcCwgdGhlcmUgd2VyZSBubyBleHByZXNzaW9uIHZhbHVlcyB0aGF0IHdlcmUgbm90IHVuaXF1ZS4gSSB3b3VsZCd2ZSBzdW1tZWQgdGhlbSB3aXRob3V0IGFkanVzdG1lbnRzLCBhZ2FpbnN0IHRoZSByZWNvbW1lbmRhdGlvbnMgb2YgcGVvcGxlIG9uIGJpb2luZm9ybWF0aWNzIGZvcnVtcywgYXNzdW1pbmcgdGhleSB3ZXJlIGFsdGVybmF0aXZlIHNwbGljZXMgb2YgdHJhbnNjcmlwdHMgb2Ygc2ltaWxhciBsZW5ndGguCgoqIFdlcmUgdGhlcmUgZXhwcmVzc2lvbiB2YWx1ZXMgdGhhdCBjb3VsZCBub3QgYmUgbWFwcGVkIHRvIGN1cnJlbnQgSFVHTyBzeW1ib2xzPwoKICAgIFRoZXJlIHdlcmUgbWFueSBleHByZXNzaW9uIHZhbHVlcyB0aGF0IGNvdWxkIG5vdCBiZSBtYXBwZWQgdG8gY3VycmVudCBIVUdPIHN5bWJvbHMuIE1vc3Qgb2YgdGhlbSBhcHBlYXIgdG8gYmUgaW4gbm9uLWNvZGluZyByZWdpb25zIHNvIEkgYW0gbm90IHdvcnJpZWQuCgoqIEhvdyBtYW55IG91dGxpZXJzIHdlcmUgcmVtb3ZlZD8KCiAgICBJIGRpZG4ndCByZW1vdmUgYW55IG91dGxpZXJzIG9mIGV4dHJlbWVseSBoaWdoIGV4cHJlc3Npb24sIGJ1dCBhbG1vc3QgdHdvLXRoaXJkcyBvZiB0aGUgZGF0YXNldCB3YXMgcmVtb3ZlZCBiZWNhdXNlIG9mIGxvdyBleHByZXNzaW9uLgoKKiBIb3cgZGlkIHlvdSBoYW5kbGUgcmVwbGljYXRlcz8KCiAgICBTaW5jZSB0aGUgZXhwZXJpbWVudCB3YXMgcmVsYXRpdmVseSBzaW1wbGUsIEkgc2ltcGx5IG1hZGUgV1QgYW5kIEhPTSBncm91cHMgZm9yIGNvbnRyb2wgYW5kIGV4cGVyaW1lbnRhbCBzYW1wbGVzIHdoZW4gYW5hbHl6aW5nIHVzaW5nIGVkZ2VSLgoKKiBXaGF0IGlzIHRoZSBmaW5hbCBjb3ZlcmFnZSBvZiB5b3VyIGRhdGFzZXQ/CgogICAgT3V0IG9mIGByIGxlbmd0aChncmVwKCJFTlNHIiwgY291bnRzJEdlbmVpZCkpYCBnZW5lcyB3ZSBzdGFydGVkIHdpdGgsIHdlIGVuZGVkIHVwIHdpdGggYHIgbnJvdyhmaW5hbF9hMV9kYXRhKWAgZ2VuZXMuIFRoYXQgaXMgYHIgbnJvdyhmaW5hbF9hMV9kYXRhKSAqIDEwMCAvIGxlbmd0aChncmVwKCJFTlNHIiwgY291bnRzJEdlbmVpZCkpYCUgY292ZXJhZ2UuCiAgICAKIyBSZWZlcmVuY2VzCgpDdW5uaW5naGFtLCBGLiwgQWNodXRoYW4sIFAuLCBBa2FubmksIFcuLCBBbGxlbiwgSi4sIEFtb2RlLCBNLiwgQXJtZWFuLCBJLiBNLiwgLiAuIC4gRmxpY2VrLCBQLiAoMjAxOCkuIEVuc2VtYmwgMjAxOS4gTnVjbGVpYyBBY2lkcyBSZXNlYXJjaCwgNDcsIEQ3NDUtRDc1MS4gZG9pOjEwLjEwOTMvbmFyL2dreTExMTMKCkV4dGVybmFsIFJOQSBjb250cm9scyBjb25zb3J0aXVtLiBSZXRyaWV2ZWQgZnJvbSBodHRwczovL3d3dy5uaXN0Lmdvdi9wcm9ncmFtcy1wcm9qZWN0cy9leHRlcm5hbC1ybmEtY29udHJvbHMtY29uc29ydGl1bQoKSEdOQyBEYXRhYmFzZSwgSFVHTyBHZW5lIE5vbWVuY2xhdHVyZSBDb21taXR0ZWUgKEhHTkMpLCBFdXJvcGVhbiBNb2xlY3VsYXIgQmlvbG9neSBMYWJvcmF0b3J5LCBFdXJvcGVhbiBCaW9pbmZvcm1hdGljcyBJbnN0aXR1dGUgKEVNQkwtRUJJKSwgV2VsbGNvbWUgR2Vub21lIENhbXB1cywgSGlueHRvbiwgQ2FtYnJpZGdlIENCMTAgMVNELCBVbml0ZWQgS2luZ2RvbS4gaHR0cHM6Ly93d3cuZ2VuZW5hbWVzLm9yZy4KCkppbWJvdSBbT25saW5lIG5hbWVdLiAoMjAxNSkuIFF1ZXN0aW9uOiBBZ2dyZWdhdGUgbXVsdGlwbGUgcm93cyBiYXNlZCBvbiBjb21tb24gdmFsdWVzIGluIGdpdmVuIGNvbHVtbnMuIFJldHJpZXZlZCBmcm9tIGh0dHBzOi8vd3d3LmJpb3N0YXJzLm9yZy9wLzE2NzAyOC8KCk1hY0RvbmFsZCwgSi4gVy4gKDIwMTYpLiBRdWVzdGlvbjogRmlsdGVyaW5nIHJlYWQgY291bnRzIG1hdHJpeDogSG93IHRvIGRlYWwgd2l0aCBkdXBsaWNhdGVkIGdlbmUgc3ltYm9scywgZGlmZmVyZW50IEVOU0VNQkwgaWRzLiBSZXRyaWV2ZWQgZnJvbSBodHRwczovL3N1cHBvcnQuYmlvY29uZHVjdG9yLm9yZy9wLzkwMTg1Lwo=